package com.graden.background.common.utils.http;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.httpclient.protocol.Protocol;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Map;

/**
 * <p>
 * 高并发HttpClient工具类
 * </p>
 *
 * @author zjf
 * @since 2019-03-18
 */
@Component
@Slf4j
public class HttpClientTemplate {

	private static final String DEFAULT_CHARSET = "UTF-8";

	private MultiThreadedHttpConnectionManager multiThreadConnManager;

	private HttpClient client;

	public HttpClientTemplate() {

		HttpConnectionManagerParams params = new HttpConnectionManagerParams();
		params.setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler());
		params.setMaxTotalConnections(10);
		params.setDefaultMaxConnectionsPerHost(5);
		params.setSoTimeout(10 * 1000);
		params.setConnectionTimeout(10 * 1000);

		multiThreadConnManager = new MultiThreadedHttpConnectionManager();
		multiThreadConnManager.setParams(params);

	}

	private HttpClient getHttpClient() {

		if (client == null)
			synchronized (this) {
				client = new HttpClient(multiThreadConnManager);
				HttpClientParams params = new HttpClientParams();
				params.setContentCharset(DEFAULT_CHARSET);
				client.setParams(params);
			}
		return client;
	}

	//生成get方法
	private GetMethod genMethod(String baseUrl, String queryString) {

		GetMethod get = new GetMethod(baseUrl);
		get.setQueryString(queryString);

		return get;

	}

	//生成get方法
	private GetMethod genMethod(String baseUrl, Map<String, Object> paramMap) {

		GetMethod get = new GetMethod(baseUrl);

		HttpMethodParams params = new HttpMethodParams();
		params.setContentCharset(DEFAULT_CHARSET);

		get.setParams(params);

		if (paramMap != null) {
			NameValuePair[] nvp = new NameValuePair[paramMap.values().size()];
			int i = 0;

			for (Map.Entry<String, Object> entry: paramMap.entrySet()) {
				nvp[i++] = new NameValuePair(entry.getKey(), entry.getValue().toString());
			}
			get.setQueryString(nvp);
		}

		return get;

	}

	public String execute(String baseUrl, Map<String, Object> paramMap) {

		if(log.isDebugEnabled()) {
			log.debug("[sending] url={}, param={}", baseUrl, JSON.toJSONString(paramMap));
		}

		try {
			GetMethod method = genMethod(baseUrl, paramMap);
			String result = execute(method);

			if(log.isDebugEnabled()) {
				log.debug("[receiving] result ={}, url={}, param={}",result, baseUrl, JSON.toJSONString(paramMap));
			}

			return result;
		}
		catch(Exception e) {
			log.error("[http exception] url={}, param={}", baseUrl, JSON.toJSONString(paramMap), e);
			return null;
		}

	}

	//json格式请求
	public String executeJson(String baseUrl, Map<String, Object> paramMap) {
		try {
			if(log.isDebugEnabled()) {
				log.debug("[sending json] url={}, param={}", baseUrl, JSON.toJSONString(paramMap));
			}

			JSONObject json = new JSONObject();
			json.putAll(paramMap);

			PostMethod method = new PostMethod(baseUrl);

			RequestEntity requestEntity = new StringRequestEntity(json.toString(), "application/json", DEFAULT_CHARSET);
			method.setRequestEntity(requestEntity);

			String result = execute(method);

			if(log.isDebugEnabled()) {
				log.debug("[receiving json] result ={}, url={}, param={}",result, baseUrl, JSON.toJSONString(paramMap));
			}

			return result;

		} catch(Exception e) {
			log.error("[http post exception] url={}, param={}", baseUrl, JSON.toJSONString(paramMap), e);
			return null;
		}
	}

	//post请求
	public String executePost(String baseUrl, Map<String, Object> paramMap){

		try {
			if(log.isDebugEnabled()) {
				log.debug("[sending post] url={}, param={}", baseUrl, JSON.toJSONString(paramMap));
			}

			PostMethod method = new PostMethod(baseUrl);
			HttpMethodParams params = new HttpMethodParams();
			params.setContentCharset(DEFAULT_CHARSET);
			method.setParams(params);

			addParameters(method,paramMap);

			String result = execute(method);

			if(log.isDebugEnabled()) {
				log.debug("[receiving post] result ={}, url={}, param={}",result, baseUrl, JSON.toJSONString(paramMap));
			}
			return result;
		} catch(Exception e) {
			log.error("[http post exception] url={}, param={}", baseUrl, JSON.toJSONString(paramMap), e);
			return null;
		}

	}



	public String executePost(String baseUrl, Map<String, Object> paramMap,Map<String, Object> headerParams) {
		try {
			if(log.isDebugEnabled()) {
				log.debug("[sending head] url={}, param={}, head={}", baseUrl, JSON.toJSONString(paramMap), JSON.toJSONString(headerParams));
			}

			PostMethod method = new PostMethod(baseUrl);
			HttpMethodParams params = new HttpMethodParams();
			params.setContentCharset(DEFAULT_CHARSET);
			method.setParams(params);

			addParameters(method,paramMap);

			if (headerParams != null) {
				method.getParams().setVirtualHost(headerParams.get("hostUrl").toString());
			}

			String result = execute(method, headerParams);

			if(log.isDebugEnabled()) {
				log.debug("[receiving head] result ={}, url={}, param={}, head={}",result, baseUrl, JSON.toJSONString(paramMap), JSON.toJSONString(headerParams));
			}

			return result;
		} catch(Exception e) {
				log.error("[http post exception] url={}, param={}", baseUrl, JSON.toJSONString(paramMap), e);
				return null;
		}
	}



	public String execute(String url) throws IOException {
		GetMethod method = new GetMethod(url);
		return execute(method);
	}



	public String execute(String baseUrl, String queryString) throws IOException {
		GetMethod method = genMethod(baseUrl, queryString);
		return execute(method);
	}



	private String execute(HttpMethod method) throws IOException {
		try {
			return getResponseBodyResult(getHttpClient(),method);
		} finally {
			if (null != method) {
				method.releaseConnection();
			}
		}

	}



	private String execute(HttpMethod method, Map<String, Object> headerParams) throws IOException {

		try {
			HttpClient httpClient = getHttpClient();

			if (headerParams != null) {
				HostConfiguration hf = new HostConfiguration();
				if (headerParams.get("hostUrl") != null) {
					hf.setHost(headerParams.get("hostUrl").toString(), 80, Protocol.getProtocol("http"));
				}

				httpClient.setHostConfiguration(hf);
			}
			return getResponseBodyResult(httpClient,method);

		} finally {
			if (null != method) {
				method.releaseConnection();
			}
		}

	}

	public String getResponseBodyResult(HttpClient httpClient,HttpMethod method) throws IOException {

		int statusCode = httpClient.executeMethod(method);

		if (200 != statusCode) {
			throw new HttpException("status code: " + statusCode);
		} else {

			InputStream resStream = method.getResponseBodyAsStream();
			BufferedReader br = new BufferedReader(new InputStreamReader(resStream, DEFAULT_CHARSET));

			StringBuilder builder = new StringBuilder();
			String resTemp = "";

			while ((resTemp = br.readLine()) != null) {
				builder.append(resTemp);
			}

			return builder.toString();
		}

	}

	public void addParameters(PostMethod method,Map<String, Object> paramMap){
		if (paramMap != null) {

			NameValuePair[] nvp = new NameValuePair[paramMap.values().size()];
			int i = 0;

			for (Map.Entry<String, Object> entry: paramMap.entrySet()) {
				nvp[i++] = new NameValuePair(entry.getKey(), entry.getValue() .toString());
			}
			method.addParameters(nvp);
		}
	}

	public static class Factory {

		private static HttpClientTemplate instance = new HttpClientTemplate();

		public static HttpClientTemplate getClient() {
			return Factory.instance;
		}
	}
}
